/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.mgt.server.service.system.impl;

import static com.baomidou.mybatisplus.extension.repository.IRepository.DEFAULT_BATCH_SIZE;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.iwindplus.base.domain.enums.BizCodeEnum;
import com.iwindplus.base.domain.exception.BizException;
import com.iwindplus.mgt.domain.constant.MgtConstant.RedisCacheConstant;
import com.iwindplus.mgt.domain.dto.system.ServerApiDTO;
import com.iwindplus.mgt.domain.dto.system.ServerApiSearchDTO;
import com.iwindplus.mgt.domain.vo.system.ServerApiGroupVO;
import com.iwindplus.mgt.domain.vo.system.ServerApiPageVO;
import com.iwindplus.mgt.domain.vo.system.ServerApiVO;
import com.iwindplus.mgt.server.dal.model.system.ServerApiDO;
import com.iwindplus.mgt.server.dal.model.system.ServerDO;
import com.iwindplus.mgt.server.dal.repository.system.ServerApiRepository;
import com.iwindplus.mgt.server.service.system.ServerApiService;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 服务API业务层接口实现类.
 *
 * @author zengdegui
 * @since 2020/3/25
 */
@Slf4j
@Service
@CacheConfig(cacheNames = {RedisCacheConstant.CACHE_SERVER_API})
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class ServerApiServiceImpl implements ServerApiService {

    private final ServerApiRepository serverApiRepository;
    
    @Caching(
        evict = {
            @CacheEvict(cacheNames = {RedisCacheConstant.CACHE_SERVER_API}, allEntries = true),
        }
    )
    @Override
    public boolean save(ServerApiDTO entity) {
        entity.setHideFlag(Boolean.FALSE);
        this.serverApiRepository.getControllerNameIsExist(entity.getControllerName().trim(), entity.getServerId());
        this.serverApiRepository.getApiNameIsExist(entity.getApiName().trim(), entity.getServerId());
        this.serverApiRepository.getApiUrlIsExist(entity.getApiUrl().trim(), entity.getServerId());
        entity.setSeq(this.serverApiRepository.getNextSeq());
        ServerApiDO model = BeanUtil.copyProperties(entity, ServerApiDO.class);
        boolean data = this.serverApiRepository.save(model);
        entity.setId(model.getId());
        return data;
    }

    @Caching(
        evict = {
            @CacheEvict(cacheNames = {RedisCacheConstant.CACHE_SERVER_API}, allEntries = true),
        }
    )
    @Override
    public boolean saveOrEditBatch(List<ServerApiDTO> entities) {
        if (CollUtil.isEmpty(entities)) {
            return Boolean.FALSE;
        }
        final Integer nextSeq = this.serverApiRepository.getNextSeq();
        AtomicInteger seq = new AtomicInteger(nextSeq);
        List<ServerApiDTO> saveList = new ArrayList<>(10);
        List<ServerApiDTO> editList = new ArrayList<>(10);
        entities.forEach(entity -> {
            final ServerApiDO data = this.serverApiRepository.getOne(Wrappers.lambdaQuery(ServerApiDO.class)
                .eq(ServerApiDO::getApiUrl, entity.getApiUrl())
                .eq(ServerApiDO::getServerId, entity.getServerId())
                .select(ServerApiDO::getId, ServerApiDO::getVersion));
            if (Objects.isNull(data)) {
                entity.setSeq(seq.getAndIncrement());
                saveList.add(entity);
            } else {
                entity.setId(data.getId());
                entity.setVersion(data.getVersion());
                editList.add(entity);
            }
        });
        if (CollUtil.isNotEmpty(saveList)) {
            List<ServerApiDO> doList = BeanUtil.copyToList(saveList, ServerApiDO.class);
            this.serverApiRepository.saveBatch(doList, DEFAULT_BATCH_SIZE);
        }
        if (CollUtil.isNotEmpty(editList)) {
            List<ServerApiDO> doList = BeanUtil.copyToList(editList, ServerApiDO.class);
            this.serverApiRepository.updateBatchById(doList, DEFAULT_BATCH_SIZE);
        }
        return Boolean.TRUE;
    }

    @Caching(
        evict = {
            @CacheEvict(cacheNames = {RedisCacheConstant.CACHE_SERVER_API}, allEntries = true),
        }
    )
    @Override
    public boolean removeByIds(List<Long> ids) {
        List<ServerApiDO> list = this.serverApiRepository.listByIds(ids);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }

        return this.serverApiRepository.removeByIds(ids);
    }

    @Caching(
        evict = {
            @CacheEvict(cacheNames = {RedisCacheConstant.CACHE_SERVER_API}, allEntries = true),
        }
    )
    @Override
    public boolean edit(ServerApiDTO entity) {
        ServerApiDO data = this.serverApiRepository.getById(entity.getId());
        if (Objects.isNull(data)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        if (CharSequenceUtil.isNotBlank(entity.getControllerName()) && !CharSequenceUtil.equals(data.getControllerName(),
            entity.getControllerName().trim())) {
            this.serverApiRepository.getControllerNameIsExist(entity.getControllerName().trim(), data.getServerId());
        }
        if (CharSequenceUtil.isNotBlank(entity.getApiName()) && !CharSequenceUtil.equals(data.getApiName(), entity.getApiName().trim())) {
            this.serverApiRepository.getApiNameIsExist(entity.getApiName().trim(), data.getServerId());
        }
        if (CharSequenceUtil.isNotBlank(entity.getApiUrl()) && !CharSequenceUtil.equals(data.getApiUrl(), entity.getApiUrl().trim())) {
            this.serverApiRepository.getApiUrlIsExist(entity.getApiUrl().trim(), data.getServerId());
        }
        if (Objects.isNull(entity.getVersion())) {
            entity.setVersion(data.getVersion());
        }
        ServerApiDO model = BeanUtil.copyProperties(entity, ServerApiDO.class);
        boolean result = this.serverApiRepository.updateById(model);
        return result;
    }

    @Caching(
        evict = {
            @CacheEvict(cacheNames = {RedisCacheConstant.CACHE_SERVER_API}, allEntries = true),
        }
    )
    @Override
    public boolean editHideFlag(Long id, Boolean hideFlag) {
        ServerApiDO data = this.serverApiRepository.getById(id);
        if (Objects.isNull(data)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        if (hideFlag.equals(data.getHideFlag())) {
            throw new BizException(BizCodeEnum.ALREADY_OPERATED);
        }
        ServerApiDO param = new ServerApiDO();
        param.setId(id);
        param.setHideFlag(hideFlag);
        param.setVersion(data.getVersion());
        this.serverApiRepository.updateById(param);
        return Boolean.TRUE;
    }

    @Override
    public IPage<ServerApiPageVO> page(ServerApiSearchDTO entity) {
        PageDTO<ServerApiPageVO> page = new PageDTO<>(entity.getCurrent(), entity.getSize());
        page.setOptimizeCountSql(Boolean.FALSE);
        page.setOptimizeJoinOfCountSql(Boolean.FALSE);
        MPJLambdaWrapper<ServerApiDO> queryWrapper = new MPJLambdaWrapper<ServerApiDO>()
            .selectAll(ServerApiDO.class)
            .leftJoin(ServerDO.class, ServerDO::getId, ServerApiDO::getServerId)
            .selectAs(ServerDO::getRouteId, ServerApiPageVO::getServerRouteId)
            .selectAs(ServerDO::getName, ServerApiPageVO::getServerName)
            .selectAs(ServerDO::getSeq, ServerApiPageVO::getServerSeq)
            .orderByDesc(ServerApiDO::getModifiedTime);
        if (Objects.nonNull(entity.getServerId())) {
            queryWrapper.eq(ServerDO::getId, entity.getServerId());
        }
        if (CharSequenceUtil.isNotBlank(entity.getControllerName())) {
            queryWrapper.like(ServerApiDO::getControllerName, entity.getControllerName().trim());
        }
        if (CharSequenceUtil.isNotBlank(entity.getApiName())) {
            queryWrapper.like(ServerApiDO::getApiName, entity.getApiName().trim());
        }
        if (CharSequenceUtil.isNotBlank(entity.getApiUrl())) {
            queryWrapper.like(ServerApiDO::getApiUrl, entity.getApiUrl().trim());
        }
        return this.serverApiRepository.selectJoinListPage(page, ServerApiPageVO.class, queryWrapper);
    }

    @Cacheable(key = "#root.methodName + '_' + #p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public List<ServerApiGroupVO> listByServerId(Long serverId) {
        LambdaQueryWrapper<ServerApiDO> queryWrapper = Wrappers.lambdaQuery(ServerApiDO.class)
            .eq(ServerApiDO::getHideFlag, Boolean.FALSE)
            .eq(ServerApiDO::getServerId, serverId)
            .select(ServerApiDO::getId, ServerApiDO::getControllerName, ServerApiDO::getRequestMethod, ServerApiDO::getApiName,
                ServerApiDO::getApiUrl)
            .orderByAsc(List.of(ServerApiDO::getControllerName, ServerApiDO::getRequestMethod, ServerApiDO::getApiName, ServerApiDO::getApiUrl));
        List<ServerApiDO> list = this.serverApiRepository.list(queryWrapper);
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        return list.stream().filter(Objects::nonNull)
            .collect(Collectors.groupingBy(ServerApiDO::getControllerName,
                LinkedHashMap::new, Collectors.toList()))
            .entrySet().stream().filter(Objects::nonNull)
            .map(e -> ServerApiGroupVO.builder()
                .controllerName(e.getKey())
                .apis(BeanUtil.copyToList(e.getValue(), ServerApiGroupVO.ApiVO.class))
                .build())
            .collect(Collectors.toList());
    }

    @Cacheable(key = "#root.methodName + '_' + #p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public ServerApiVO getDetail(Long id) {
        ServerApiDO data = this.serverApiRepository.getById(id);
        if (Objects.isNull(data)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        return BeanUtil.copyProperties(data, ServerApiVO.class);
    }

}
